//: [Previous](@previous)
//: ## 初始化器
//: [Next](@next)
/*
 类 / 结构体 / 枚举 都可以定义初始化器
 类 有2种初始化器:
  1.指定初始化器
 init(参数列表) {
    初始化代码
 }
  2.便捷初始化器, 便捷初始化器的初始化代码里面必须调用一次指定初始化器
 convenience init(参数列表) {
    初始化代码
 }
 
 - 每个类至少要有一个指定初始化器, 指定初始化器是类的主要初始化器
 - 默认初始化器总是类的指定初始化器
 - 类偏向于少量指定初始化器, 一个类通常只有一个指定初始化器
 
 初始化器的相互调用原则
 1. 指定初始化器必须从它的直系父类调用指定初始化器(这个类有父类的话)
 2.便捷初始化器必须从自己的类里调用另一个初始化器(便捷初始化器只能调用自己类里面的其他初始化器,不能调用父类的初始化器)
 3. 便捷初始化器最终必须调用一个指定初始化器(但是要确保一定要调用到一次指定初始化器)
 
 
 */
class A {
    var age: Int
    var height: Int
    
    //指定初始化器(主要初始化器)
    init(age: Int, height: Int) {
        self.age = age
        self.height = height
        //...很重要的代码,必须实现
    }// 因为我重写了init,所以系统默认init(){}已经失效
    
    //便捷初始化器,是一定要在要执行指定初始化器的.假如有这种场景,这个类你觉得有很多必须实现的代码,你可以写在上面这个指定初始化器里面,然后再把其他所有初始化器都改成便捷初始化器,这样子就可以确保所有人调用这个类初始化的时候必须会调用到唯一那个指定初始化器必须要实现了你要实现的代码
    convenience init(age: Int) {
        self.init(age: age, height: 0)
    }
    convenience init(height: Int) {
        self.init(age: 0, height: height)
    }
    convenience init() {
        self.init(age: 0, height: 0)
    }
    //上面这3种convenience init就确保了调用这个类的所有初始化方法都必须走一次指定初始化器的代码了
}
var a1 = A(age: 10, height: 20)
var a2 = A(age: 10)
var a3 = A(height: 20)
var a4 = A()
//A这个类的4种初始化方法,其实都走了指定初始化器代码

//父类 子类的类
class B {
    var age: Int
    init(age: Int) {
        self.age = age
    }
}
// 子类的指定初始化器, 必须调用父类的指定初始化器, 调用的时候顺序是先初始化好子类自己的(先搞好自己的事情), 再调用父类的
class bb : B {
    var weight: Int
    init(age: Int, weight: Int) {
        self.weight = weight
        super.init(age: age)
    }
}


//: ## 初始化的过程
/*
 - swift为了编码安全,保证初始化的过程中,设定了 两段式初始化和安全检查
 
 两段式初始化
 第一阶段:初始化所有存储属性
 1. 外层调用指定\便捷初始化器, 比如var student = Student()
 2. 分配内存给实例, 但未开始初始化
 3. 指定初始化器确保当前类定义的所有存储属性都初始化
 4. 假如当前类有直系父类, 指定初始化器调用父类的初始化器, 不断向上调用, 形成初始化器链
 
 第二阶段:设置新的存储属性
 1. 从顶部的初始化器往下(最大的父类), 链中的每1个指定初始化器都有机会进一步定制实例, 也能使用self(访问 / 修改它的属性 / 调用它的实例方法等等), 说白了就是确保所有属性都初始化之后,才可以进行任何别的操作
 class B {
     var age: Int
     init(age: Int) {
         self.age = age
        //现在已经初始化完成了,下面可以定制我自己的代码
        self.age = 1111111
        self.test()
     }
    func test(){
    }
 }
 2.最终,链中的任何便捷初始化器都有机会定制实例以及使用self
 
 
 安全检查
 1. 指定初始化器必须保证在调用父类初始化器之前, 其所在的类的所有存储属性都已经初始化完成 (子类,调用初始化的时候,先要把自己的所有存储属性都初始化好,然后再调用父类的初始化去初始化继承的属性)
 2. 指定初始化器必须先调用完父类初始化器, 然后才能为继承的属性设置新值
    (就是子类需要操作继承自父类的值,必须在已经调用完父类初始化器之后,这不是废话吗..)
 3. 便捷初始化器必须先调用同类中的其他初始化器, 然后才能为任一属性设置新值或者调用方法之类的,反正就是用self
    (没初始化好,怎么设置值,还是废话)
 */
//: ## 重写初始化器
/*
 1. 当重写 父类 的 指定初始化器 时, 必须加上 override (即使子类是在重写便自己的捷初始化器)
 2. 如果子类写了一个匹配父类的便捷初始化器的初始化器(看起来像重写), 不用加上override
   (因为父类的编辑初始化器不能被子类直接调用,所以用不了super.父类,所以严格来说,子类无法重写父类的便捷初始化器)
 */

//: ## 自动继承
/*
 1. 如果子类没有自定义任何指定初始化器, 它会自动继承父类所有指定初始化器
 2. 如果子类提供了父类所有指定初始化器的实现(继承或者重写),子类就会自动继承父类的所有便携初始化器
 */
//: ## required(必需的)
/*
 1. 用required修饰的指定初始化器(便捷初始化器没用), 表明其所有子类都必须实现该初始化器(通过自动继承或者重写实现)
 2. 如果子类重写父类的required修饰的指定初始化器, 也必须加上required, 不用加override, 因为子类写父类的初始化器这种情况肯定是重写
 3. 什么时候使用这个修饰关键词, 就是你希望这个类的所有子类都可以使用这个父类的这种指定初始化器去初始化
 */
class C {
    required init() {
    }
    init(age: Int) {
    }
}
class cc : C {
    //这个代码区假如什么初始化器都不写,就会触发子类自动继承父类,这样子父类的初始化器都有了,但是假如写了自定义的初始化器,就继承不了了,就需要重写父类的required
    init(No: Int) {
        super.init(age: 0)
    }
    required init() {
        super.init()
    }
}
var ccc = cc()
//: ## 属性观察器
/*
 父类的属性在它自己的初始化器中赋值, 不会触发自己的属性观察器, 但在子类的初始化器中赋值, 就会触发父类的自己的熟悉观察器
 ps:一个类的属性在自己的初始化方法里面第一次赋值, 不会触发自己的熟悉观察器, 并没有违背这个原理
 */
class D {
    var age: Int {
        willSet {
            print("fu D willSet", newValue)
        }
        didSet {
            print("fu D didSet", oldValue, age)
        }
    }
    init() {
        self.age = 1 // 初始化值,这里不会触发观察器
    }
}
var dd = D()
class ddd : D {
    override init() {
        super.init()
        self.age = 2 // 子类此时赋值, 162行age已经初始化了=1, 这里相当于第二次赋值了, 所以会触发age的所在父类的属性观察器
    }
}
var dddd = ddd()
//fu D willSet 2
//fu D didSet 1 2
//: ## 可失败初始化器
/*
 1. 类, 结构体, 枚举 都可以使用 init? 定义可失败初始化器, 其实就是把这个对象设为可选项, 因为要返回nil只有可选项可以
 系统代码的常用可失败初始化器:
 var num = int("123")
 
 enum Anser : Int { //枚举给了原始值int,就会默认0,1
    case wrong, right
 }
 var an = Answer(rawValue:1) //通过枚举的原始值来获取枚举对象,有可能失败
 2.不允许同时定义 参数标签/ 参数个数/ 参数类型 相同的 可失败初始化器和非可失败初始化器 (就是定义都一样,就是?的不一样,类似 重写 )
 3.可以使用 init! 定义隐式解包的可失败初始化器
 4.可失败初始化器可以调用非可失败初始化器, 非可失败初始化器调用可失败初始化器需要进行解包(可以使用init!)
 5.如果一个初始化器 调用 一个可失败初始化器 导致此次初始化失败, 那么整个初始化过程都会失败停止,初始化里面后续的代码不会运行(都return nil了)
 6.可以用一个非可失败初始化器重写一个可失败初始化器, 但是反过来不行
 */
class E {
    var name: String
    init?(name: String) { // 加上问号, 判断.isEmpty, 返回nil
        if name.isEmpty {
            return nil
        }
        self.name = name
    }
}
print("-----")
var e1 = E(name: "")
print(e1)
//nil
print(e1?.name)
//nil
var e2 = E(name: "Jack")
print(e2?.name)
//Optional("Jack")
//: ## 反初始化器 (deinit)
/*
 deinit 叫做反初始化器, 类似 OC中的dealloc方法
 - 当 类 的实例对象被释放内存时, 就会调用实例对象的deinit方法(注意是类的对象才有,别的对象没有)
 - deinit 不接受任何参数, 不能写小括号, 不能自行调用
 - 父类的deinit能被子类继承
 - 子类的deinit实现执行完毕后,会再调用父类的deinit
 
 对象什么时候销毁:
 1. 没有指针指向,比如本来var stu = Stu() 但是下一步stu = Person(), 这样子Stu类型的那个对象就没有指针指向了, 这样子就会被销毁
 2. 对象被=nil, 比如 可选项的类型, 就可以设置=nil, 这个时候这个对象也会被销毁(强指针不再指向该对象)
 3. 在函数里面new一个对象, 这个对象就相当于局部变量, 这个函数运行的时候这个对象会创建, 函数运行完之后这个对象就会变销毁
 */
class F {
    deinit {
        print("F 对象销毁")
    }
}
class ff : F {
    deinit {
        print("ff 对象销毁")
    }
}
func testF() {
    var fff = ff()
}
print("------")
testF()
//ff 对象销毁
//F  对象销毁
//先销毁子类自己的,再销毁父类的,和初始化器的一样,先初始化完自己的所有属性,再初始化父类的属性
//:## 可选链 (Optional chaining)
class Car { var price = 0 }
class Dog { var weight = 0 }
class Person {
    var name: String = ""
    var dog: Dog = Dog()
    var car: Car? = Car()
    func age() -> Int{ 18 }
    func eat() { print("Person eat")}
    subscript(index: Int) -> Int { index }
}
var person1: Person? = Person() //创建一个叫person1可选类型, 里面包装着一个Person对象
var age = person1?.age() //Int? 因为person1是可选类型, 有可能是nil, 所以nil.age()是不行的, nil不可以调用任何值, 所以需要解包, 假如用!解包(person1!.age()), 因为person1有可能是nil, 所以其实是不安全的. 这个情况就可以使用?解包(person1?.age()), person1?会先判断有值就返回值就可以顺利调用.age(), 假如没值会直接nil,这样子就不会调用.age()直接没有后续, 这样子就安全. age应该是个什么的类型呢? 因为person?有可能是返回nil, 而不是nil的情况下是返回18, 同时满足nil和18的类型就只有Int?, 所以age就会被系统自动定义成Int?
var age1 = person1!.age() //Int?
var name = person1?.name //String?
var index = person1?[6] //Int?

func getName() -> String {
    "Jack"
}
person1?.name = getName()
//如果person1是nil, 不会调用getName
/*
 总结:
 1. 如果可选项为nil, 那么这个可选项调用 方法/ 下标/ 属性 都会失败, 结果为nil
 2. 如果可选项不为nil, 那么这个可选项调用 方法/ 下标/ 属性 都会成功, 结果会被包装成可选项
  2.1 如果结果本来就是可选项, 不会再进行包装(比如结果是Int?, 就直接是Int?, 不会是Int??)
 */
//:## 可选项绑定
var result = person1?.eat()
//eat()是没有返回值的方法, 在swift就是返回void也是等于返回空元祖 (), 因为空元祖是可以赋值的, 所以直接用于判断这个可选项能不能解包成功, 并且拿到里面的值来使用
if let result1 = person1?.eat() {
    print("调用eat成功")
} else {
    print("调用eat失败")
}
//这种result1用不了的, 也可以用_代替
if let _ = person1?.eat() {
    print("调用eat成功")
} else {
    print("调用eat失败")
}
//需要使用的
if let age1 = person1?.age() {
    print("年龄是:",age1)
//年龄是: 18
} else {
    print("调用age失败")
}

var dog1 = person1?.dog //Dog?
var weight = person1?.dog.weight //Int? 因为person1?是可选项,后面跟着很长的属性关系,这种就叫做可选链, 这种是只能返回可选项
var price = person1?.car?.price //Int?
/*
 多个 ? 可以链接在一起
 如果链中任何一个节点为nil, 那么整条链就会调用失败
 */
// 举例元素是字典的数组, 关键字是字符串, value是数组,
var scores = [
    "Jack" : [11, 22, 33],
    "Rose" : [44, 55, 66]
]
var array = scores["Jack"]
print(array)
//Optional([11, 22, 33]), 因为系统怕key是乱写的, 导致取值不超过越界, 所以字典取值是自动被系统变成可选项
//scores["Jack"][0] 直接取数组元素会报错, 因为scores["Jack"]的取值需要加上?或者!
print(scores["Jack"]?[0]) // Optional(11)

//举例, 一个字典, key是String ,value是函数(接受2个Int,返回1个Int)
var dict: [String : (Int,Int)->Int ] = [
    "Sum" : (+),
    "difference" : (-)
]
var resultDict = dict["Sum"]?(10, 20)//Optional(30), Int? , 因为dict["Sum"]? 有可能是一个函数了, 是个可选项, 这个函数是可以调用后面的2个Int, 但是得判断这个可选项是否为nil
